#include "ViewChangeCertificate.hpp"

using namespace BFT::order::statemanagement;

ViewChangeCertificate::ViewChangeCertificate(uint32_t _view, bool _primary)
{
	primary = _primary;
	viewNumber = _view;
	vc.reset();
	vcDigest.reset();
	finished = false;
	hasAcksFlag = false;
	vca.resize(BFT::Parameters::getOrderCount());
	ackCache.resize(BFT::Parameters::getOrderCount());
}

//*  Add the view change to the certificate.  throws an exception
//*  if the view does not match.  if the view change matches the
//*  digest, then keep the other data - if it does not, then wipe the
//*  other data and proceed.
void ViewChangeCertificate::addViewChange(boost::shared_ptr<BFT::order::messages::ViewChange> _vc2)
{
	if (_vc2.get()->getView() != viewNumber)
	{
		fprintf(stderr, "ViewChangeCertificate::addViewChange says: trying to add a View change with a different view number\n");
		exit(157);
	}
	
	boost::shared_ptr<BFT::messages::Digest> tmp (new BFT::messages::Digest(_vc2.get()->getBytes(), DIGEST_SIZE) );
	if (!tmp.get()->equals(vcDigest))
		clear();
		
	vc = _vc2;
	vcDigest = tmp;
	
	for(unsigned int i=0; i < ackCache.size(); i++)
	{
		if(ackCache.at(i).use_count()!=0)
		{	
//			fprintf(stderr,"+++++ adding a cached ack ++++\n");
			addAck(ackCache.at(i));
		}
	}
}

void ViewChangeCertificate::addAck(boost::shared_ptr<BFT::order::messages::ViewChangeAck> _vcack)
{
	if (!primary)
	{
		fprintf(stderr,"ViewChangeCertificate::addAck says: only the primary accept vc acks\n");
		exit(158);
	}
	
	if(_vcack.get()->getViewNo() != viewNumber)
	{
		fprintf(stderr,"ViewChangeCertificate::addAck says: trying to add a vcack with a different view number\n");
		exit(159);
	}
	
//	if(vcDigest.use_count() == 0)
	if(vc.use_count() == 0)
	{
		ackCache.at( _vcack.get()->getSender() ) = _vcack;
		return;
	}
	
	if(vc.get()->getSendingReplica() != _vcack.get()->getChangingReplica())
	{
		fprintf(stderr,"\n\n!@#$^&*()_+_((*&^$#@))\n\n");
		fprintf(stderr,"ViewChangeCertificate::addAck says: trying to add a vcack not for the right replica. changing replica: %d, \
		vcack changing replica: %d, vcack sender: %d\n", vc.get()->getSendingReplica(), 
				_vcack.get()->getChangingReplica(), _vcack.get()->getSender());
		exit(160);
	}
	
	if (_vcack.get()->getVCDigest().get()->equals(vcDigest) )
	{
		fprintf(stderr, "ViewChangeCertificate::addAck says: adding ack sent by %d for vc sent by %d\n",
				_vcack.get()->getSender(), _vcack.get()->getChangingReplica() );
		vca.at(_vcack.get()->getSender()) = _vcack;
	}
	else
	{
		fprintf(stderr,"ViewChangeCertificate::addAck says: something wrong with _vcack\n");
		exit(161);
	
	}
}

void ViewChangeCertificate::addConfirmView(boost::shared_ptr<BFT::order::messages::ConfirmView> _cv)
{
	if ( _cv.get()->getViewNo() != viewNumber )
	{
		fprintf(stderr,"ViewChangeCertificate::addConfirmView says: numbers shoukd match\n");
		exit(162);
	
	}
	confirmView = _cv;
}

bool ViewChangeCertificate::hasAcks()
{
	if( hasAcksFlag )
		return true;
		
	int count = 0;
	for(unsigned int i=0; i<vca.size(); i++)
	{
		if(vca.at(i).use_count() != 0)
			count++;
	}
	
	hasAcksFlag = vc.use_count() != 0 && count >= BFT::Parameters::largeOrderQuorumSize();
	if(hasAcksFlag)
		fprintf(stderr,"VCC for view: %d, sender: %d -- vc.use_count(): %d and count: %d. acks received from %d %d %d %d\n",
			vc.get()->getView(), vc.get()->getSendingReplica(), (int)vc.use_count(), count, 
			vca.at(0).use_count() != 0?1:0, vca.at(1).use_count() != 0?1:0, 
			vca.at(2).use_count() != 0?1:0, vca.at(3).use_count() != 0?1:0);
		
	return hasAcksFlag;
}

void ViewChangeCertificate::clear()
{
	vcDigest.reset();
	finished = false;
	hasAcksFlag = false;
	vc.reset();
	for (unsigned int i = 0; i< vca.size(); i++)
	{
		vca.at(i).reset();
	}
}
